﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.Statistics
{
    /// <summary>
    /// This class provides the creation of a trend function, which based on a specified timeseries.
    /// </summary>
    [Serializable]
    public class TrendFunctionCreator
    {
        /// <summary>
        /// The timeseries on which the trend function should base.
        /// </summary>
        private TimeSeries series;

        /// <summary>
        /// This field holds the model function.
        /// </summary>
        private ModelFunction modelFunction;

        /// <summary>
        /// Internal regression engine.
        /// </summary>
        private readonly CurveFitting fittingEngine;

        /// <summary>
        /// Initializes a new instance of the <see cref="TrendFunctionCreator"/> class.
        /// </summary>
        /// <param name="series">The timeseries on which the trend function should base.</param>
        /// <param name="modelFunction">The specified model function.</param>
        public TrendFunctionCreator(TimeSeries series, ModelFunction modelFunction)
        {
            if (series == (TimeSeries) null)
            {
                throw new ArgumentNullException("series");
            }

            if (series.Data == (GeneralVector) null)
            {
                throw new ArgumentNullException("series");
            }

            this.series = series;
            this.fittingEngine = new CurveFitting(modelFunction);
        }

        /// <summary>
        /// Gets a value indicating whether [precision error].
        /// </summary>
        /// <value>True if the computation has a precision error and the trend function could not 
        /// be created otherwise, false.</value>
        public bool PrecisionError
        {
            get { return this.fittingEngine.PrecisionError; }
        }

        /// <summary>
        /// Gets or sets the timeseries on which the trend function should base.
        /// </summary>
        /// <value>The timeseries on which the trend function should base.</value>
        public TimeSeries Series
        {
            get { return this.series; }
            set { this.series = value; }
        }

        /// <summary>
        /// Gets the estimated error of the model function parameters.
        /// </summary>
        /// <value>The estimated error of the model function parameters.</value>
        public double EstimatedError
        {
            get { return this.fittingEngine.EstimatedError; }
        }

        /// <summary>
        /// Gets or sets the specified model function.
        /// </summary>
        /// <value>The specified model function.</value>
        public ModelFunction ModelFunction
        {
            get { return this.modelFunction; }
            set { this.modelFunction = value; }
        }

        /// <summary>
        /// Creates the trend function.
        /// </summary>
        /// <param name="parameters">Estimated values for the parameters of the trend function.</param>
        /// <param name="iterations">The maximum number of iterations to use.</param>
        /// <returns>The parameters of the trend function.</returns>
        public GeneralVector CreateTrendFunction(GeneralVector parameters, int iterations)
        {
            return this.CreateTrendFunction(parameters, iterations, 1e-5);
        }

        /// <summary>
        /// Creates the trend function.
        /// </summary>
        /// <param name="numberOfparameters">The number of parameters of the trend function.</param>
        /// <param name="iterations">The maximum number of iterations to use.</param>
        /// <returns>The parameters of the trend function.</returns>
        public GeneralVector CreateTrendFunction(int numberOfparameters, int iterations)
        {
            return this.CreateTrendFunction(numberOfparameters, iterations, 1e-5);
        }

        /// <summary>
        /// Creates the trend function.
        /// </summary>
        /// <param name="numberOfparameters">The number of parameters of the trend function.</param>
        /// <param name="iterations">The maximum number of iterations to use.</param>
        /// <param name="precision">The precision to use.</param>
        /// <returns>The parameters of the trend function.</returns>
        public GeneralVector CreateTrendFunction(int numberOfparameters, int iterations, double precision)
        {
            GeneralVector parameters = new GeneralVector(numberOfparameters);

            for (int i = 0; i < numberOfparameters; i++)
            {
                parameters[i] = 1;
            }

            return this.CreateTrendFunction(parameters, iterations, precision);
        }

        /// <summary>
        /// Creates the trend function.
        /// </summary>
        /// <param name="parameters">Estimated values for the parameters of the trend function.</param>
        /// <param name="iterations">The maximum number of iterations to use.</param>
        /// <param name="precision">The precision to use.</param>
        /// <returns>The parameters of the trend function.</returns>
        public GeneralVector CreateTrendFunction(GeneralVector parameters, int iterations, double precision)
        {
            return this.fittingEngine.FitCurve(this.series.GenerateIndexVector(), this.series.Data, parameters,
                                               iterations, precision);
        }
    }
}